{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1J9jK_CIldWe"
      },
      "source": [
        "Copyright 2021 Google LLC\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n",
        "\n",
        "https://www.apache.org/licenses/LICENSE-2.0\n",
        "\n",
        "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lxTfMlmywY2i"
      },
      "source": [
        "# Setup (run first)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GLNs0Qeskfvq"
      },
      "outputs": [],
      "source": [
        "!pip install folktables\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from sklearn import neural_network, linear_model, cluster\n",
        "from sklearn.utils._testing import ignore_warnings\n",
        "from sklearn.exceptions import ConvergenceWarning\n",
        "from collections import defaultdict\n",
        "from scipy.special import logit, expit\n",
        "from itertools import product\n",
        "clip = lambda x: np.maximum(np.minimum(x, 1-1e-7), 1e-7)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gv-kug2phvC9"
      },
      "source": [
        "# Uncorrelated synthetic data + standard attribute inference"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "h6zx60iUH-kg"
      },
      "outputs": [],
      "source": [
        "all_res = defaultdict(list)\n",
        "model_fns = {'lr': linear_model.LogisticRegression, 'nn': neural_network.MLPClassifier}\n",
        "for (datasize, d, model_name, expid) in product([25, 100, 1000], [10, 100, 1000], model_fns, range(10)):\n",
        "  if expid==0:\n",
        "    print(datasize, d, model_name)\n",
        "  np.random.seed(expid)\n",
        "  x = -1 + 2*np.random.randint(0, 2, size=(datasize, d))\n",
        "  y = np.random.randint(0, 2, size=datasize)\n",
        "  model_fn = model_fns[model_name]\n",
        "  target_col = 0\n",
        "\n",
        "  def get_scores_basic(model, x, y):\n",
        "    preds = []\n",
        "    for i in range(2):\n",
        "      x[:, target_col] = -1  + 2*i\n",
        "      preds.append(model.predict_proba(x))\n",
        "    preds = np.array(preds)\n",
        "    return preds\n",
        "\n",
        "  @ignore_warnings(category=ConvergenceWarning)\n",
        "  def run_experiment(x, y, model_fn):\n",
        "    ss_inds = np.random.choice(x.shape[0], x.shape[0]//2, replace=False)\n",
        "    ss_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "    ss_mask[ss_inds] = True\n",
        "\n",
        "    x_trn, y_trn = x[ss_mask], y[ss_mask]\n",
        "    model = model_fn().fit(x_trn, y_trn)\n",
        "    test_preds = get_scores_basic(model, x[~ss_mask].copy(), y[~ss_mask])\n",
        "    test_preds = np.multiply(test_preds, np.eye(2)[y[~ss_mask]][None, :, :]).sum(axis=2)\n",
        "    test_acc = ((-1+2*test_preds.argmax(0))==x[~ss_mask, target_col]).mean()\n",
        "\n",
        "    train_preds = get_scores_basic(model, x[ss_mask].copy(), y[ss_mask])\n",
        "    train_preds = np.multiply(train_preds, np.eye(2)[y[ss_mask]][None, :, :]).sum(axis=2)\n",
        "    train_acc = ((-1+2*train_preds.argmax(0))==x[ss_mask, target_col]).mean()\n",
        "    return train_acc, test_acc\n",
        "\n",
        "  res_li = []\n",
        "  for i in range(10):\n",
        "    res_li.append(run_experiment(x, y, model_fn))\n",
        "  all_res[datasize, d, model_name].append(np.mean(res_li, axis=0))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7vGjgw8NsOFj"
      },
      "outputs": [],
      "source": [
        "print(\"n, d, model\")\n",
        "for res in all_res:\n",
        "  mean_res = np.mean(all_res[res], axis=0)[0]\n",
        "  print(res, mean_res)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L8aLKVY8h51K"
      },
      "source": [
        "# Uncorrelated synthetic data + poisoning attribute inference"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "background_save": true
        },
        "id": "EMnrVylNh-Df"
      },
      "outputs": [],
      "source": [
        "all_res = defaultdict(list)\n",
        "model_fns = {'lr': linear_model.LogisticRegression, 'nn': neural_network.MLPClassifier}\n",
        "for datasize, d, model_name in product([25, 100, 1000], [10, 100, 1000], model_fns):\n",
        "  print(datasize, d, model_name)\n",
        "  res_li = []\n",
        "  for expid in range(20):\n",
        "    np.random.seed(expid)\n",
        "    x = -1 + 2*np.random.randint(0, 2, size=(datasize, d))\n",
        "    y = np.random.randint(0, 2, size=datasize)\n",
        "    model_fn = model_fns[model_name]\n",
        "    target_col = 0\n",
        "\n",
        "    def make_poison(x, y, repeats=1):\n",
        "      pois_x, pois_y = x.copy(), y.copy()\n",
        "      pois_x[:, target_col] = -1\n",
        "      pois_y = 1-pois_y\n",
        "      pois_x = np.concatenate([pois_x]*repeats)\n",
        "      pois_y = np.concatenate([pois_y]*repeats)\n",
        "      return pois_x, pois_y\n",
        "\n",
        "    def get_scores_basic(model, x, y):\n",
        "      preds = []\n",
        "      for i in range(2):\n",
        "        x[:, target_col] = -1  + 2*i\n",
        "        preds.append(model.predict_proba(x))\n",
        "      preds = np.array(preds)\n",
        "      return preds\n",
        "\n",
        "    @ignore_warnings(category=ConvergenceWarning)\n",
        "    def run_experiment(x, y, model_fn):\n",
        "      ss_inds = np.random.choice(x.shape[0], x.shape[0]//2, replace=False)\n",
        "      pois_inds = ss_inds[:5]\n",
        "      ss_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      pois_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      ss_mask[ss_inds] = True\n",
        "      pois_mask[pois_inds] = True\n",
        "\n",
        "      pois_x, pois_y = make_poison(x[pois_mask], y[pois_mask], repeats=2)\n",
        "      x_trn = np.concatenate([x[ss_mask], pois_x])\n",
        "      y_trn = np.concatenate([y[ss_mask], pois_y])\n",
        "      model = model_fn().fit(x_trn, y_trn)\n",
        "      test_preds = get_scores_basic(model, x[~ss_mask].copy(), y[~ss_mask])\n",
        "      test_preds = np.multiply(test_preds, np.eye(2)[y[~ss_mask]][None, :, :]).sum(axis=2)\n",
        "      test_acc = ((-1+2*test_preds.argmax(0))==x[~ss_mask, target_col]).mean()\n",
        "\n",
        "      train_preds = get_scores_basic(model, x[ss_mask \u0026 ~pois_mask].copy(), y[ss_mask \u0026 ~pois_mask])\n",
        "      train_preds = np.multiply(train_preds, np.eye(2)[y[ss_mask \u0026 ~pois_mask]][None, :, :]).sum(axis=2)\n",
        "      train_acc = ((-1+2*train_preds.argmax(0))==x[ss_mask \u0026 ~pois_mask, target_col]).mean()\n",
        "\n",
        "      poised_preds = get_scores_basic(model, x[pois_mask].copy(), y[pois_mask].copy())\n",
        "      poised_preds = np.multiply(poised_preds, np.eye(2)[y[pois_mask]][None, :, :]).sum(axis=2)\n",
        "      pois_preds = get_scores_basic(model, pois_x.copy(), pois_y)\n",
        "      pois_preds = np.multiply(pois_preds, np.eye(2)[pois_y][None, :, :]).sum(axis=2)\n",
        "      pois_preds = pois_preds[:, :poised_preds.shape[1]]\n",
        "      #print(pois_preds - poised_preds)\n",
        "      #print(poised_preds[1] - poised_preds[0])\n",
        "      #print(pois_preds[1]-pois_preds[0])\n",
        "      pred_diffs = poised_preds - pois_preds\n",
        "      #print(pred_diffs[0] - pred_diffs[1])\n",
        "      #print(poised_preds, pois_preds)\n",
        "      #print(x[pois_mask, target_col])\n",
        "      train_scores, train_targs = train_preds[1]-train_preds[0], x[ss_mask \u0026 ~pois_mask, target_col]\n",
        "      pois_scores, pois_targs = pred_diffs[1]-pred_diffs[0], x[pois_mask, target_col]\n",
        "      return train_acc, test_acc, pois_scores, pois_targs, train_scores, train_targs\n",
        "\n",
        "    for i in range(20):\n",
        "      res_li.append(run_experiment(x, y, model_fn)[2:])\n",
        "  trn_scores = np.array([v[2] for v in res_li]).ravel()\n",
        "  trn_targs = np.array([v[3] for v in res_li]).ravel()\n",
        "  trn_acc = np.mean((trn_scores\u003enp.median(trn_scores))==(trn_targs\u003e0))\n",
        "\n",
        "  scores = np.array([v[0] for v in res_li]).ravel()\n",
        "  targs = np.array([v[1] for v in res_li]).ravel()\n",
        "  pois_acc = np.mean((scores\u003enp.median(scores))==(targs\u003e0))\n",
        "  all_res[datasize, d, model_name] = trn_acc, pois_acc\n",
        "  print(datasize, d, model_name, trn_acc, pois_acc)\n",
        "for key in all_res:\n",
        "  print(key, all_res[key])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CtVAZfpO1RTO"
      },
      "source": [
        "# Correlated synthetic data + poisoning attribute inference"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "n53xh77p3QMA"
      },
      "outputs": [],
      "source": [
        "all_res = defaultdict(list)\n",
        "model_fns = {'lr': linear_model.LogisticRegression, 'nn': neural_network.MLPClassifier}\n",
        "for datasize, d, model_name in product([25, 100, 1000], [10, 100, 1000], model_fns):\n",
        "  print(datasize, d, model_name)\n",
        "  res_li = []\n",
        "  for expid in range(20):\n",
        "    np.random.seed(expid)\n",
        "    x = -1 + 2*np.random.randint(0, 2, size=(datasize, d))\n",
        "    # add correlation\n",
        "    w = np.zeros(d)\n",
        "    w[:5] = 1\n",
        "    preds = np.dot(x, w)\n",
        "    preds = expit(preds)\n",
        "    y = (np.random.random(size=preds.shape) \u003c preds).astype(np.int)\n",
        "    model_fn = model_fns[model_name]\n",
        "    target_col = 0\n",
        "\n",
        "    def make_poison(x, y, repeats=1):\n",
        "      pois_x, pois_y = x.copy(), y.copy()\n",
        "      pois_x[:, target_col] = -1\n",
        "      pois_y = 1-pois_y\n",
        "      pois_x = np.concatenate([pois_x]*repeats)\n",
        "      pois_y = np.concatenate([pois_y]*repeats)\n",
        "      return pois_x, pois_y\n",
        "\n",
        "    def get_scores_basic(model, x, y):\n",
        "      preds = []\n",
        "      for i in range(2):\n",
        "        x[:, target_col] = -1  + 2*i\n",
        "        preds.append(model.predict_proba(x))\n",
        "      preds = np.array(preds)\n",
        "      return preds\n",
        "\n",
        "    @ignore_warnings(category=ConvergenceWarning)\n",
        "    def run_experiment(x, y, model_fn):\n",
        "      ss_inds = np.random.choice(x.shape[0], x.shape[0]//2, replace=False)\n",
        "      pois_inds = ss_inds[:5]\n",
        "      ss_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      pois_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      ss_mask[ss_inds] = True\n",
        "      pois_mask[pois_inds] = True\n",
        "\n",
        "      pois_x, pois_y = make_poison(x[pois_mask], y[pois_mask], repeats=2)\n",
        "      x_trn = np.concatenate([x[ss_mask], pois_x])\n",
        "      y_trn = np.concatenate([y[ss_mask], pois_y])\n",
        "      model = model_fn().fit(x_trn, y_trn)\n",
        "      test_preds = get_scores_basic(model, x[~ss_mask].copy(), y[~ss_mask])\n",
        "      test_preds = np.multiply(test_preds, np.eye(2)[y[~ss_mask]][None, :, :]).sum(axis=2)\n",
        "      test_acc = ((-1+2*test_preds.argmax(0))==x[~ss_mask, target_col]).mean()\n",
        "\n",
        "      train_preds = get_scores_basic(model, x[ss_mask \u0026 ~pois_mask].copy(), y[ss_mask \u0026 ~pois_mask])\n",
        "      train_preds = np.multiply(train_preds, np.eye(2)[y[ss_mask \u0026 ~pois_mask]][None, :, :]).sum(axis=2)\n",
        "      train_acc = ((-1+2*train_preds.argmax(0))==x[ss_mask \u0026 ~pois_mask, target_col]).mean()\n",
        "\n",
        "      poised_preds = get_scores_basic(model, x[pois_mask].copy(), y[pois_mask].copy())\n",
        "      poised_preds = np.multiply(poised_preds, np.eye(2)[y[pois_mask]][None, :, :]).sum(axis=2)\n",
        "      pois_preds = get_scores_basic(model, pois_x.copy(), pois_y)\n",
        "      pois_preds = np.multiply(pois_preds, np.eye(2)[pois_y][None, :, :]).sum(axis=2)\n",
        "      pois_preds = pois_preds[:, :poised_preds.shape[1]]\n",
        "      #print(pois_preds - poised_preds)\n",
        "      #print(poised_preds[1] - poised_preds[0])\n",
        "      #print(pois_preds[1]-pois_preds[0])\n",
        "      pred_diffs = poised_preds - pois_preds\n",
        "      #print(pred_diffs[0] - pred_diffs[1])\n",
        "      #print(poised_preds, pois_preds)\n",
        "      #print(x[pois_mask, target_col])\n",
        "      train_scores, train_targs = train_preds[1]-train_preds[0], x[ss_mask \u0026 ~pois_mask, target_col]\n",
        "      pois_scores, pois_targs = pred_diffs[1]-pred_diffs[0], x[pois_mask, target_col]\n",
        "      return train_acc, test_acc, pois_scores, pois_targs, train_scores, train_targs\n",
        "\n",
        "    for i in range(20):\n",
        "      res_li.append(run_experiment(x, y, model_fn)[2:])\n",
        "  trn_scores = np.array([v[2] for v in res_li]).ravel()\n",
        "  trn_targs = np.array([v[3] for v in res_li]).ravel()\n",
        "  trn_acc = np.mean((trn_scores\u003enp.median(trn_scores))==(trn_targs\u003e0))\n",
        "\n",
        "  scores = np.array([v[0] for v in res_li]).ravel()\n",
        "  targs = np.array([v[1] for v in res_li]).ravel()\n",
        "  pois_acc = np.mean((scores\u003enp.median(scores))==(targs\u003e0))\n",
        "  all_res[datasize, d, model_name] = trn_acc, pois_acc\n",
        "  print(datasize, d, model_name, trn_acc, pois_acc)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7qAAYC5L_6nh"
      },
      "outputs": [],
      "source": [
        "# baseline accuracy\n",
        "newd = 10\n",
        "x = -1 + 2*np.random.randint(0, 2, size=(10000, newd))\n",
        "# add correlation\n",
        "w = np.zeros(newd)\n",
        "w[:5] = 1\n",
        "preds = np.dot(x, w)\n",
        "preds = expit(preds)\n",
        "y = (np.random.random(size=preds.shape) \u003c preds)\n",
        "print((y==(x[:, 0]\u003e0)).mean())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4hHFOqyWb7Fn"
      },
      "source": [
        "# Adult dataset standard attribute inference"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GH21mgeW4WfK"
      },
      "outputs": [],
      "source": [
        "from folktables import ACSDataSource, ACSIncome\n",
        "\n",
        "data_source = ACSDataSource(survey_year='2018', horizon='1-Year', survey='person')\n",
        "ca_data = data_source.get_data(states=[\"CA\"], download=True)\n",
        "ca_features, ca_labels, _ = ACSIncome.df_to_numpy(ca_data)\n",
        "def feature_fn(preproc_fea):\n",
        "  all_feature_names = ['AGEP', 'COW', 'SCHL', 'MAR', 'OCCP', 'POBP', 'RELP', 'WKHP', 'SEX', 'RAC1P']\n",
        "  to_categorical = ['COW', 'SCHL', 'MAR', 'RAC1P', 'SEX']\n",
        "  to_remove = ['POBP', 'RELP', 'OCCP']\n",
        "  cols = {}\n",
        "  all_features = []\n",
        "  for i, fea_name in enumerate(all_feature_names):\n",
        "    fea_start = len(all_features)\n",
        "    if fea_name in to_remove:\n",
        "      continue\n",
        "    elif fea_name in to_categorical:\n",
        "      uniques = np.unique(preproc_fea[:, i], return_counts=True)\n",
        "      for fea_value in uniques[0]:\n",
        "        all_features.append(preproc_fea[:, i]==fea_value)\n",
        "    else:\n",
        "      all_features.append(preproc_fea[:, i])\n",
        "      continue\n",
        "    cols[fea_name] = (fea_start, len(all_features))\n",
        "    # age, type of job, schooling level, marriage, occupation code, birth state, unk, hours worked, sex, race\n",
        "    #print(['AGEP', 'COW', 'SCHL', 'MAR', 'OCCP', 'POBP', 'RELP', 'WKHP', 'SEX', 'RAC1P'][i])\n",
        "    #print(np.unique(ca_features[:, i], return_counts=True))\n",
        "  return np.array(all_features).T, cols\n",
        "\n",
        "ca_postproc, cols = feature_fn(ca_features)\n",
        "print(ca_postproc.shape)\n",
        "print(cols)\n",
        "\n",
        "# Plug-in your method for tabular datasets\n",
        "model = linear_model.LogisticRegression()\n",
        "\n",
        "# Train on CA data\n",
        "model.fit(ca_postproc, ca_labels)\n",
        "\n",
        "print(model.score(ca_postproc, ca_labels))\n",
        "\n",
        "target_col = 'SEX'\n",
        "\n",
        "start_i, end_i = cols[target_col]\n",
        "target_col_vals = ca_postproc[:, start_i:end_i]\n",
        "unique_vals, unique_counts = np.unique(target_col_vals, axis=0, return_counts=True)\n",
        "min_count = unique_counts.min()\n",
        "print(unique_vals, unique_counts)\n",
        "balanced_fea, balanced_lab = [], []\n",
        "for unique_val in unique_vals:\n",
        "  match_inds = np.where(ca_postproc[:, start_i:end_i]==unique_val)[0]\n",
        "  print(match_inds.shape)\n",
        "  sampled_inds = np.random.choice(match_inds, min_count, replace=False)\n",
        "  print(sampled_inds.shape)\n",
        "  balanced_fea.append(ca_postproc[sampled_inds])\n",
        "  balanced_lab.append(ca_labels[sampled_inds])\n",
        "balanced_fea, balanced_lab = np.concatenate(balanced_fea), np.concatenate(balanced_lab)\n",
        "print(balanced_fea.shape, balanced_lab.shape)\n",
        "print(np.unique(balanced_fea[:, start_i:end_i], axis=0, return_counts=True))\n",
        "\n",
        "def get_scores_basic_adult(model, x, y):\n",
        "    new_x = x.copy()\n",
        "    preds = []\n",
        "    start_i, end_i = cols[target_col]\n",
        "    for i in range(end_i-start_i):\n",
        "      x[:, start_i:end_i] = np.eye(end_i-start_i)[None, i]\n",
        "      preds.append(model.predict_proba(x))\n",
        "    preds = np.array(preds)\n",
        "    return preds"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8YptGwSAc4og"
      },
      "outputs": [],
      "source": [
        "all_res = defaultdict(list)\n",
        "model_fns = {'lr': linear_model.LogisticRegression, 'nn': neural_network.MLPClassifier}\n",
        "for n, model_name in product([25, 100, 1000], model_fns):\n",
        "  res_li = []\n",
        "  for expid in range(400):\n",
        "    np.random.seed(expid)\n",
        "    x = balanced_fea\n",
        "    y = balanced_lab.astype(np.int)\n",
        "    model_fn = model_fns[model_name]\n",
        "    start_i, end_i = cols[target_col]\n",
        "\n",
        "\n",
        "    def make_poison(x, y, repeats=1):\n",
        "      pois_x, pois_y = x.copy(), y.copy()\n",
        "      start_i, end_i = cols[target_col]\n",
        "      pois_x[:, start_i:end_i] = np.eye(end_i-start_i)[None, 0]\n",
        "      pois_y = 1-pois_y\n",
        "      pois_x = np.concatenate([pois_x]*repeats)\n",
        "      pois_y = np.concatenate([pois_y]*repeats)\n",
        "      return pois_x, pois_y\n",
        "\n",
        "    @ignore_warnings(category=ConvergenceWarning)\n",
        "    def run_experiment(x, y, model_fn):\n",
        "      ss_inds = np.random.choice(x.shape[0], n, replace=False)\n",
        "      pois_inds = ss_inds[:5]\n",
        "      ss_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      pois_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      ss_mask[ss_inds] = True\n",
        "      pois_mask[pois_inds] = True\n",
        "\n",
        "      pois_x, pois_y = make_poison(x[pois_mask], y[pois_mask], repeats=1)\n",
        "      x_trn = np.concatenate([x[ss_mask], pois_x])\n",
        "      y_trn = np.concatenate([y[ss_mask], pois_y])\n",
        "      model = model_fn().fit(x_trn, y_trn)\n",
        "      test_preds = get_scores_basic_adult(model, x[~ss_mask].copy(), y[~ss_mask])\n",
        "      test_preds = np.multiply(test_preds, np.eye(2)[y[~ss_mask]][None, :, :]).sum(axis=2)\n",
        "      test_acc = ((test_preds.argmax(0))==x[~ss_mask, start_i:end_i].argmax(1)).mean()\n",
        "\n",
        "      train_preds = get_scores_basic_adult(model, x[ss_mask \u0026 ~pois_mask].copy(), y[ss_mask \u0026 ~pois_mask])\n",
        "      train_preds = np.multiply(train_preds, np.eye(2)[y[ss_mask \u0026 ~pois_mask]][None, :, :]).sum(axis=2)\n",
        "      train_acc = ((train_preds.argmax(0))==x[ss_mask \u0026 ~pois_mask, start_i:end_i].argmax(1)).mean()\n",
        "\n",
        "      poised_preds = get_scores_basic_adult(model, x[pois_mask].copy(), y[pois_mask].copy())\n",
        "      poised_preds = np.multiply(poised_preds, np.eye(2)[y[pois_mask]][None, :, :]).sum(axis=2)\n",
        "      pois_preds = get_scores_basic_adult(model, pois_x.copy(), pois_y)\n",
        "      pois_preds = np.multiply(pois_preds, np.eye(2)[pois_y][None, :, :]).sum(axis=2)\n",
        "      pois_preds = pois_preds[:, :poised_preds.shape[1]]\n",
        "      #print(pois_preds)\n",
        "      pred_diffs = poised_preds - pois_preds\n",
        "      #print(x[pois_mask, start_i:end_i])\n",
        "      train_scores1, train_scores0, train_targs = train_preds[1], train_preds[0], x[ss_mask \u0026 ~pois_mask, start_i:end_i].argmax(1)\n",
        "      pois_scores1, pois_scores0, pois_targs = pois_preds[1], pois_preds[0], x[pois_mask, start_i:end_i].argmax(1)\n",
        "      #print(pois_scores)\n",
        "      return train_acc, test_acc, pois_scores1, pois_scores0, pois_targs, train_scores1, train_scores0, train_targs\n",
        "\n",
        "    for i in range(1):\n",
        "      res_li.append(run_experiment(x, y, model_fn)[2:])\n",
        "  trn_scores1 = np.array([v[3] for v in res_li]).ravel()\n",
        "  trn_scores0 = np.array([v[4] for v in res_li]).ravel()\n",
        "  trn_targs = np.array([v[5] for v in res_li]).ravel()\n",
        "  #print((trn_scores1 - np.mean(trn_scores1))[:5], (trn_scores0 - np.mean(trn_scores0))[:5], trn_targs[:5])\n",
        "  trn_score_mat = np.array([trn_scores1, trn_scores0]).T\n",
        "  first_half = trn_score_mat.shape[0]//2\n",
        "  trn_lr = linear_model.LogisticRegression().fit(trn_score_mat[:first_half], trn_targs[:first_half])\n",
        "  trn_acc = trn_lr.score(trn_score_mat[first_half:], trn_targs[first_half:])\n",
        "  #print(trn_lr.intercept_, trn_lr.coef_, trn_lr.score(trn_score_mat, trn_targs))\n",
        "\n",
        "  scores1 = np.array([v[0] for v in res_li]).ravel()\n",
        "  scores0 = np.array([v[1] for v in res_li]).ravel()\n",
        "  targs = np.array([v[2] for v in res_li]).ravel()\n",
        "  score_mat = np.array([scores1, scores0]).T\n",
        "  first_half = score_mat.shape[0]//2\n",
        "  pois_lr = linear_model.LogisticRegression().fit(score_mat[:first_half], targs[:first_half])\n",
        "  pois_acc = pois_lr.score(score_mat[first_half:], targs[first_half:])\n",
        "  #print(pois_lr.intercept_, pois_lr.coef_, pois_lr.score(score_mat, targs))\n",
        "  #pois_acc = np.mean((scores\u003enp.median(scores))==(targs\u003e0))\n",
        "  all_res[n, model_name] = trn_acc, pois_acc\n",
        "  print(n, model_name, trn_acc, pois_acc)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SEeMJazpvLVa"
      },
      "source": [
        "# Adult data canaries"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IfmOvDr8vNue"
      },
      "outputs": [],
      "source": [
        "from folktables import ACSDataSource, ACSIncome\n",
        "\n",
        "data_source = ACSDataSource(survey_year='2018', horizon='1-Year', survey='person')\n",
        "ca_data = data_source.get_data(states=[\"CA\"], download=True)\n",
        "ca_features, ca_labels, _ = ACSIncome.df_to_numpy(ca_data)\n",
        "def feature_fn(preproc_fea):\n",
        "  all_feature_names = ['AGEP', 'COW', 'SCHL', 'MAR', 'OCCP', 'POBP', 'RELP', 'WKHP', 'SEX', 'RAC1P']\n",
        "  to_categorical = ['COW', 'SCHL', 'MAR', 'RAC1P', 'SEX']\n",
        "  to_remove = ['POBP', 'RELP', 'OCCP']\n",
        "  cols = {}\n",
        "  all_features = []\n",
        "  for i, fea_name in enumerate(all_feature_names):\n",
        "    fea_start = len(all_features)\n",
        "    if fea_name in to_remove:\n",
        "      continue\n",
        "    elif fea_name in to_categorical:\n",
        "      uniques = np.unique(preproc_fea[:, i], return_counts=True)\n",
        "      for fea_value in uniques[0]:\n",
        "        all_features.append(preproc_fea[:, i]==fea_value)\n",
        "    else:\n",
        "      all_features.append(preproc_fea[:, i])\n",
        "      continue\n",
        "    cols[fea_name] = (fea_start, len(all_features))\n",
        "    # age, type of job, schooling level, marriage, occupation code, birth state, unk, hours worked, sex, race\n",
        "    #print(['AGEP', 'COW', 'SCHL', 'MAR', 'OCCP', 'POBP', 'RELP', 'WKHP', 'SEX', 'RAC1P'][i])\n",
        "    #print(np.unique(ca_features[:, i], return_counts=True))\n",
        "  return np.array(all_features).T, cols\n",
        "\n",
        "ca_postproc, cols = feature_fn(ca_features)\n",
        "print(ca_postproc.shape)\n",
        "print(cols)\n",
        "\n",
        "# Plug-in your method for tabular datasets\n",
        "model = linear_model.LogisticRegression()\n",
        "\n",
        "# Train on CA data\n",
        "model.fit(ca_postproc, ca_labels)\n",
        "\n",
        "print(model.score(ca_postproc, ca_labels))\n",
        "\n",
        "target_col = 'SEX'\n",
        "\n",
        "start_i, end_i = cols[target_col]\n",
        "target_col_vals = ca_postproc[:, start_i:end_i]\n",
        "unique_vals, unique_counts = np.unique(target_col_vals, axis=0, return_counts=True)\n",
        "min_count = unique_counts.min()\n",
        "print(unique_vals, unique_counts)\n",
        "balanced_fea, balanced_lab = [], []\n",
        "for unique_val in unique_vals:\n",
        "  match_inds = np.where(ca_postproc[:, start_i:end_i]==unique_val)[0]\n",
        "  print(match_inds.shape)\n",
        "  sampled_inds = np.random.choice(match_inds, min_count, replace=False)\n",
        "  print(sampled_inds.shape)\n",
        "  balanced_fea.append(ca_postproc[sampled_inds])\n",
        "  balanced_lab.append(ca_labels[sampled_inds])\n",
        "balanced_fea, balanced_lab = np.concatenate(balanced_fea), np.concatenate(balanced_lab)\n",
        "print(balanced_fea.shape, balanced_lab.shape)\n",
        "print(np.unique(balanced_fea[:, start_i:end_i], axis=0, return_counts=True))\n",
        "\n",
        "def get_scores_basic_adult(model, x, y):\n",
        "    new_x = x.copy()\n",
        "    preds = []\n",
        "    start_i, end_i = cols[target_col]\n",
        "    for i in range(end_i-start_i):\n",
        "      x[:, start_i:end_i] = np.eye(end_i-start_i)[None, i]\n",
        "      preds.append(model.predict_proba(x))\n",
        "    preds = np.array(preds)\n",
        "    return preds"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "u23fp23rwgTk"
      },
      "outputs": [],
      "source": [
        "all_res = defaultdict(list)\n",
        "model_fns = {'lr': linear_model.LogisticRegression, 'nn': neural_network.MLPClassifier}\n",
        "for n, model_name in product([25, 100, 1000], model_fns):\n",
        "  res_li = []\n",
        "  for expid in range(400):\n",
        "    np.random.seed(expid)\n",
        "    x = balanced_fea\n",
        "    y = balanced_lab.astype(np.int)\n",
        "    model_fn = model_fns[model_name]\n",
        "    start_i, end_i = cols[target_col]\n",
        "\n",
        "\n",
        "    def make_poison(x, y, repeats=1):\n",
        "      pois_x, pois_y = x.copy(), y.copy()\n",
        "      start_i, end_i = cols[target_col]\n",
        "      pois_x[:, start_i:end_i] = np.eye(end_i-start_i)[None, 0]\n",
        "      pois_y = 1-pois_y\n",
        "      pois_x = np.concatenate([pois_x]*repeats)\n",
        "      pois_y = np.concatenate([pois_y]*repeats)\n",
        "      return pois_x, pois_y\n",
        "\n",
        "    @ignore_warnings(category=ConvergenceWarning)\n",
        "    def run_experiment(x, y, model_fn):\n",
        "      ss_inds = np.random.choice(x.shape[0], n, replace=False)\n",
        "      pois_inds = ss_inds[:5]\n",
        "      ss_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      pois_mask = np.zeros(x.shape[0], dtype=np.bool)\n",
        "      ss_mask[ss_inds] = True\n",
        "      pois_mask[pois_inds] = True\n",
        "\n",
        "      pois_x, pois_y = make_poison(x[pois_mask], y[pois_mask], repeats=1)\n",
        "      x_trn = np.concatenate([x[ss_mask], pois_x])\n",
        "      y_trn = np.concatenate([y[ss_mask], pois_y])\n",
        "      model = model_fn().fit(x_trn, y_trn)\n",
        "      test_preds = get_scores_basic_adult(model, x[~ss_mask].copy(), y[~ss_mask])\n",
        "      test_preds = np.multiply(test_preds, np.eye(2)[y[~ss_mask]][None, :, :]).sum(axis=2)\n",
        "      test_acc = ((test_preds.argmax(0))==x[~ss_mask, start_i:end_i].argmax(1)).mean()\n",
        "\n",
        "      train_preds = get_scores_basic_adult(model, x[ss_mask \u0026 ~pois_mask].copy(), y[ss_mask \u0026 ~pois_mask])\n",
        "      train_preds = np.multiply(train_preds, np.eye(2)[y[ss_mask \u0026 ~pois_mask]][None, :, :]).sum(axis=2)\n",
        "      train_acc = ((train_preds.argmax(0))==x[ss_mask \u0026 ~pois_mask, start_i:end_i].argmax(1)).mean()\n",
        "\n",
        "      poised_preds = get_scores_basic_adult(model, x[pois_mask].copy(), y[pois_mask].copy())\n",
        "      poised_preds = np.multiply(poised_preds, np.eye(2)[y[pois_mask]][None, :, :]).sum(axis=2)\n",
        "      pois_preds = get_scores_basic_adult(model, pois_x.copy(), pois_y)\n",
        "      pois_preds = np.multiply(pois_preds, np.eye(2)[pois_y][None, :, :]).sum(axis=2)\n",
        "      pois_preds = pois_preds[:, :poised_preds.shape[1]]\n",
        "      #print(pois_preds)\n",
        "      pred_diffs = poised_preds - pois_preds\n",
        "      #print(x[pois_mask, start_i:end_i])\n",
        "      train_scores1, train_scores0, train_targs = train_preds[1], train_preds[0], x[ss_mask \u0026 ~pois_mask, start_i:end_i].argmax(1)\n",
        "      pois_scores1, pois_scores0, pois_targs = pois_preds[1], pois_preds[0], x[pois_mask, start_i:end_i].argmax(1)\n",
        "      #print(pois_scores)\n",
        "      return train_acc, test_acc, pois_scores1, pois_scores0, pois_targs, train_scores1, train_scores0, train_targs\n",
        "\n",
        "    for i in range(1):\n",
        "      res_li.append(run_experiment(x, y, model_fn)[2:])\n",
        "  trn_scores1 = np.array([v[3] for v in res_li]).ravel()\n",
        "  trn_scores0 = np.array([v[4] for v in res_li]).ravel()\n",
        "  trn_targs = np.array([v[5] for v in res_li]).ravel()\n",
        "  #print((trn_scores1 - np.mean(trn_scores1))[:5], (trn_scores0 - np.mean(trn_scores0))[:5], trn_targs[:5])\n",
        "  trn_score_mat = np.array([trn_scores1, trn_scores0]).T\n",
        "  first_half = trn_score_mat.shape[0]//2\n",
        "  trn_lr = linear_model.LogisticRegression().fit(trn_score_mat[:first_half], trn_targs[:first_half])\n",
        "  trn_acc = trn_lr.score(trn_score_mat[first_half:], trn_targs[first_half:])\n",
        "  #print(trn_lr.intercept_, trn_lr.coef_, trn_lr.score(trn_score_mat, trn_targs))\n",
        "\n",
        "  scores1 = np.array([v[0] for v in res_li]).ravel()\n",
        "  scores0 = np.array([v[1] for v in res_li]).ravel()\n",
        "  targs = np.array([v[2] for v in res_li]).ravel()\n",
        "  score_mat = np.array([scores1, scores0]).T\n",
        "  first_half = score_mat.shape[0]//2\n",
        "  pois_lr = linear_model.LogisticRegression().fit(score_mat[:first_half], targs[:first_half])\n",
        "  pois_acc = pois_lr.score(score_mat[first_half:], targs[first_half:])\n",
        "  #print(pois_lr.intercept_, pois_lr.coef_, pois_lr.score(score_mat, targs))\n",
        "  #pois_acc = np.mean((scores\u003enp.median(scores))==(targs\u003e0))\n",
        "  all_res[n, model_name] = trn_acc, pois_acc\n",
        "  print(n, model_name, trn_acc, pois_acc)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "attr_inf_random.ipynb",
      "provenance": [
        {
          "file_id": "1VsJkBGQjFqeoq4W9Dy4MZSecCfxLESTu",
          "timestamp": 1638549964991
        }
      ],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
